package com.ai.service.made.impl;

import com.ai.common.constant.AlgorithmConstant;
import com.ai.common.constant.FunctionTypeConstants;
import com.ai.common.constant.PlcConstants;
import com.ai.common.constant.enumP.AlarmRunStatus;
import com.ai.common.exception.ServiceException;
import com.ai.entity.alarm.AlgorithmAlarmBO;
import com.ai.entity.alarm.SignalCameraRelation;
import com.ai.entity.alarm.SignalRecord;
import com.ai.entity.cameraFunctions.functions.CameraFunctionsPO;
import com.ai.entity.cameraFunctions.timeInterval.TimeIntervalBaseVO;
import com.ai.entity.comm.SysNetty;
import com.ai.entity.function.FunctionPO;
import com.ai.entity.made.camera.CameraPO;
import com.ai.entity.made.camera.SwitchCameraAlgorithmBO;
import com.ai.entity.made.camera.SwitchCameraFunctionAlgorithmBO;
import com.ai.entity.made.camera.SwithCameraFunctionRegionsAlgorithmBO;
import com.ai.entity.network.IdentifySysToL1Message;
import com.ai.entity.network.L1ToIdentifySysMessage;
import com.ai.entity.server.ServerPO;
import com.ai.networking.netty.client.BootNettyNewLink;
import com.ai.service.made.*;
import com.ai.service.networking.ISysNettyService;
import com.ai.util.HttpRequestUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

import static com.ai.networking.netty.client.BootNettyClientChannelCache.clientSendMessage;


/**
 * @Author jiaok
 * @Date 2023-06-25 14:17
 */
@Slf4j
@Service
public class IL1SignalServiceImpl implements IL1SignalService {

    @Value("${api.ai.url}")
    private String aiUrl;

    @Autowired
    private ISysNettyService sysNettyService;
    @Resource
    private ServerService serverService;
    @Resource
    private CameraFunctionsService cameraFunctionsService;

    @Resource
    private ISignalRecordService iSignalRecordService;

    @Resource
    private FunctionService functionService;

    @Resource
    private TimeIntervalService timeIntervalService;

    @Resource
    private RegionsService regionsService;

    @Autowired
    private ISignalCameraRelationService iSignalCameraRelationService;




    /**
     * 处理接收到的信号
     *
     * @param message
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiveSignal(L1ToIdentifySysMessage message) {
        //获取当前信号对应的功能是哪个
        String messageJson = JSONObject.toJSONString(message);
        log.info("===========================  # IL1SignalServiceImpl.receiveSignal #  ===========================");
        log.info("=========================== 开始处理操作信号：{}  ===========================", messageJson);


        Integer telId = message.getTelId();
        Integer functionType = 0;
        boolean hasStopSignal = false;
        Map<String, Object> map = getHasStopSignalAndFunType(telId);
        functionType = (Integer) map.get("telId");
        hasStopSignal = (boolean) map.get("hasStopSignal");


        /**
         *获取当前开启对应功能的所有摄像头
         */
        List<CameraPO> cameraList = new ArrayList<>();

        ArrayList<Integer> funTypeList = new ArrayList<>();
        if (functionType.equals(FunctionTypeConstants.SUPERPOSE_STEEL_IDENTIFICATION)) {
            //首先判断是否为叠钢的信号 ,叠钢识别 和 钢板测距使用的是同一个信号 同一个摄像头，算法发送两个结果
            funTypeList.add(functionType);
            funTypeList.add(FunctionTypeConstants.RANGING_STEEL);
            cameraList = cameraFunctionsService.getCameraSteelList(funTypeList);
        } else {
            funTypeList.add(functionType);
            cameraList = cameraFunctionsService.getCameraSteelList(funTypeList);
        }
        log.info("===========================  获取当前开启对应功能的所有摄像头 " + cameraList + "  ===========================");

        /**
         * 记录信号
         * 如果是关闭信号，就去查找最新的同功能的记录信号最新记录
         * A3移载机自动确认,,,输入辊道叠钢 的关闭信号
         * 其他信号则需要直接新建信号记录
         * 信号记录表，在这里不进行持久化的关闭操作，交由算法识别结果进行处理， 这里只关心
         */
        ArrayList<SignalRecord> signalRecords = new ArrayList<>();
        //拼接收集信号日志
        splitSignalRecord(message, telId, funTypeList, signalRecords);
        log.info("===========================  对信号进行持久化操作  ===========================");
        boolean saveSignalRecord = iSignalRecordService.saveOrUpdateBatch(signalRecords);
        log.info("===========================  对信号进行持久化操作：" + signalRecords + ";結果为：" + saveSignalRecord + "==========================");

        /**
         * 准备算法入参
         */
        if (!CollectionUtils.isEmpty(cameraList)) {
            boolean finalHasStopSignal = hasStopSignal;
            for (CameraPO item : cameraList) {
                if (item == null) {
                    continue;
                }
                log.info("===========================  接收到信号---准备算法接口入参 ===========================");
                //算法入参基本信息
                SwitchCameraAlgorithmBO switchCameraAlgorithmBO = getSwitchCameraAlgorithmBaseBO(message, telId, item);
                /**
                 * 入参功能参数---由于存在
                 *  一个信号开启一个功能的信号 ： 统一收集到一个集合中
                 *  由于存在一个信号开启多个功能的信号 ：按照功能创建独立的集合进行收集
                 */
                ArrayList<SwitchCameraFunctionAlgorithmBO> switchCameraFunctionAlgorithmBOS = new ArrayList<>();
                ArrayList<SwitchCameraFunctionAlgorithmBO> listSTACKINGBOS = new ArrayList<>();
                ArrayList<SwitchCameraFunctionAlgorithmBO> list1104BOS = new ArrayList<>();
                switchCameraFunctionParam(funTypeList, item, switchCameraFunctionAlgorithmBOS, listSTACKINGBOS, list1104BOS);

                //校验摄像头是否绑定算法服务器
                int serverId = item.getServerId();
                if (serverId == 0) {
                    throw new ServiceException("该摄像头未绑定服务器");
                }
                ServerPO server = serverService.getById(serverId);
                String ip = server.getIp();
                Integer port = server.getPort();
                if (StringUtils.isBlank(ip) || port == null || port == 0) {
                    throw new ServiceException("该摄像头所绑定服务器未设置IP或端口");
                }

                //参数准备完毕，开始调用算法
                String url = ("http://" + ip + ":" + port + "/") + aiUrl;
                log.info("------------------------- 请求url：{} --------------------------", url);

                if (!CollectionUtils.isEmpty(signalRecords) && signalRecords.size() == 2) {
                    //一个信号触发两个功能//信号日志id
                    Integer stackingID = signalRecords.get(0).getId();
                    //保存信号与摄像头之间的关系
                    saveSignalCameraRelation(item.getCode(), item.getId(), stackingID);
                    callAlgorithm(switchCameraAlgorithmBO, stackingID, listSTACKINGBOS, signalRecords, 0, url, finalHasStopSignal, telId);

                    //信号日志id
                    Integer rangingId = signalRecords.get(1).getId();
                    //保存信号与摄像头之间的关系
                    saveSignalCameraRelation(item.getCode(), item.getId(), rangingId);
                    callAlgorithm(switchCameraAlgorithmBO, rangingId, list1104BOS, signalRecords, 1, url, finalHasStopSignal, telId);

                } else {
                    Integer recordID = signalRecords.get(0).getId();
                    SignalRecord signalRecord = signalRecords.get(0);
                    String otherPlcCode = getPlcCode(signalRecord.getSignalResource());
                    switchCameraAlgorithmBO.setTaskId(recordID);
                    switchCameraAlgorithmBO.setFunctions(switchCameraFunctionAlgorithmBOS);
                    //保存信号与摄像头之间的关系
                    saveSignalCameraRelation(item.getCode(), item.getId(), recordID);
                    log.info("=========================== 調用算法接口  ===========================");
                    requestAlgorithm(switchCameraAlgorithmBO, url, finalHasStopSignal, telId, signalRecord, otherPlcCode);
                }
            }
            ;
        }
    }

    /**
     * 开始调用算法
     *
     * @param switchCameraAlgorithmBO 算法参数
     * @param signalRecordID          信号记录id
     * @param list1104BOS             钢板测距功能的方法参数
     * @param signalRecords           信号记录集合
     * @param index                   0 叠钢 1 测距
     * @param url                     算法url
     * @param finalHasStopSignal      是否有关闭信号
     * @param telId                   接收到的plc信号
     */
    private void callAlgorithm(SwitchCameraAlgorithmBO switchCameraAlgorithmBO, Integer signalRecordID,
                               ArrayList<SwitchCameraFunctionAlgorithmBO> list1104BOS,
                               ArrayList<SignalRecord> signalRecords,
                               int index, String url, boolean finalHasStopSignal, Integer telId) {
        switchCameraAlgorithmBO.setTaskId(signalRecordID);
        switchCameraAlgorithmBO.setFunctions(list1104BOS);
        SignalRecord rangingSignalRecord = signalRecords.get(index);
        String rangingPlcCode = getPlcCode(rangingSignalRecord.getSignalResource());
        requestAlgorithm(switchCameraAlgorithmBO, url, finalHasStopSignal, telId, rangingSignalRecord, rangingPlcCode);
    }

    /**
     * 算法调用功能的配置参数
     *
     * @param funTypeList                      调用的所有功能列表
     * @param item                             算法需要开启的摄像头
     * @param switchCameraFunctionAlgorithmBOS 收集一个信号对应一个功能的方法参数
     * @param listSTACKINGBOS                  收集叠钢功能的方法参数
     * @param list1104BOS                      收集钢板测距功能的方法参数
     */
    private void switchCameraFunctionParam(ArrayList<Integer> funTypeList, CameraPO item,
                                           ArrayList<SwitchCameraFunctionAlgorithmBO> switchCameraFunctionAlgorithmBOS,
                                           ArrayList<SwitchCameraFunctionAlgorithmBO> listSTACKINGBOS,
                                           ArrayList<SwitchCameraFunctionAlgorithmBO> list1104BOS) {
        for (Integer funType : funTypeList) {
            FunctionPO functionPO = functionService.getOne(new QueryWrapper<FunctionPO>()
                    .eq("type", funType));
            Integer cameraId = item.getId();
            Integer funId = functionPO.getId();
            CameraFunctionsPO cameraFunctionsPO = cameraFunctionsService.getOne(new QueryWrapper<CameraFunctionsPO>()
                    .eq("camera_id", cameraId).eq("function_id", funId));

            SwitchCameraFunctionAlgorithmBO switchCameraFunctionAlgorithmBO = new SwitchCameraFunctionAlgorithmBO();
            switchCameraFunctionAlgorithmBO.setId(cameraFunctionsPO.getId());
            switchCameraFunctionAlgorithmBO.setCode(functionPO.getCode());
            switchCameraFunctionAlgorithmBO.setType(funType.toString());
            //算法阈值
            switchCameraFunctionAlgorithmBO.setThreshold(cameraFunctionsPO.getThreshold());

            //画框区域点集合
            List<SwithCameraFunctionRegionsAlgorithmBO> spots = regionsService.getSpots(cameraFunctionsPO.getId());
            switchCameraFunctionAlgorithmBO.setSpots(spots);
            //开启关闭时间
            List<TimeIntervalBaseVO> timeIntervalList = timeIntervalService.getTimeIntervalList(cameraFunctionsPO.getCameraId());
            switchCameraFunctionAlgorithmBO.setTimeIntervals(timeIntervalList);


            // 1104 1103 这两个功能是一个信号触发的，但是发送算法时，需要分开发送，所以这里将其装到不同的集合中备用
            if (funType.equals(FunctionTypeConstants.RANGING_STEEL)) {
                //钢板测距功能
                list1104BOS.add(switchCameraFunctionAlgorithmBO);
            } else if (funType.equals(FunctionTypeConstants.SUPERPOSE_STEEL_IDENTIFICATION)) {
                //叠钢
                listSTACKINGBOS.add(switchCameraFunctionAlgorithmBO);
            } else {
                //其他 都是一个信号对应一个功能，可以不做区分
                switchCameraFunctionAlgorithmBOS.add(switchCameraFunctionAlgorithmBO);
            }
        }
    }

    /**
     * 获取算法参数的基本信息
     *
     * @param message
     * @param telId
     * @param item
     * @return
     */
    private static SwitchCameraAlgorithmBO getSwitchCameraAlgorithmBaseBO(L1ToIdentifySysMessage message, Integer telId, CameraPO item) {
        SwitchCameraAlgorithmBO switchCameraAlgorithmBO = new SwitchCameraAlgorithmBO();
        switchCameraAlgorithmBO.setCameraId(item.getId());
        switchCameraAlgorithmBO.setUsername(item.getUsername());
        switchCameraAlgorithmBO.setPassword(item.getPassword());
        switchCameraAlgorithmBO.setIp(item.getIp());
        switchCameraAlgorithmBO.setUrl(item.getUrl());
        switchCameraAlgorithmBO.setCameraCode(item.getCode());
        //板坯号
        switchCameraAlgorithmBO.setPlanNo(message.getPlanNo());
        switchCameraAlgorithmBO.setPort(item.getPort());
        switchCameraAlgorithmBO.setStreamWay(item.getStreamWay());
        //触发类型 默认0首次触发 1校验触发  默认为0
        switchCameraAlgorithmBO.setDetectionTimes(0);
        if (message.getDoorNum() != null) {
            switchCameraAlgorithmBO.setDoorNum(message.getDoorNum().toString());
        }
        //开启算法或者关闭算法
        if (telId.equals(AlgorithmConstant.A3_TRANSFER_MACHINE_AUTOMATIC_CONFIRMATION) || telId.equals(AlgorithmConstant.TRACK_STACKING_STEEL)) {
            /**
             * 状态 2开启直播 3关闭直播 4开始算法 5关闭算法
             */
            if (null != message.getState() && message.getState().equals(1)) {
                //开启算法
                switchCameraAlgorithmBO.setState(1);
                switchCameraAlgorithmBO.setStatus(4);
            } else {
                //关闭算法
                switchCameraAlgorithmBO.setState(2);
                switchCameraAlgorithmBO.setStatus(5);
            }
        } else {
            //开启算法
            switchCameraAlgorithmBO.setState(1);
            switchCameraAlgorithmBO.setStatus(4);
        }
        return switchCameraAlgorithmBO;
    }

    /**
     * 拼接信号日志
     *
     * @param message       解析之后的plcJson
     * @param telId         操作信号
     * @param funTypeList   信号对应的功能列表
     * @param signalRecords 收集拼接后的信号日志记录
     */
    private void splitSignalRecord(L1ToIdentifySysMessage message, Integer telId, ArrayList<Integer> funTypeList, ArrayList<SignalRecord> signalRecords) {
        //A3移载机自动确认
        boolean isA3Transfer = telId.equals(AlgorithmConstant.A3_TRANSFER_MACHINE_AUTOMATIC_CONFIRMATION);
//        输入辊道叠钢
        boolean isTrackSteel = telId.equals(AlgorithmConstant.TRACK_STACKING_STEEL);
        // 是关闭信号
        boolean isClose =false;
        if (message.getState() != null){
            isClose   = message.getState().equals(AlarmRunStatus.ALARM_UN_RUNNING.getValue());
        }
        if ((isA3Transfer || isTrackSteel ) && isClose) {
            log.info("===========================  处理关闭信号  ===========================");
            //关闭信号
            for (Integer funType : funTypeList) {
                SignalRecord signalRecord = iSignalRecordService.getLastSingnalRecord(funType, message.getPlanNo(), message.getDoorNum());
                if (signalRecord == null) {
                    /**
                     * 没有找到对应的信号记录----------只收到了一个关闭信号,只有关闭信号，没有开启信号，
                     * 开启信号的关闭状态在算法识别结果地方进行逻辑处理
                     */
                    signalRecord = getSignalRecord(message, funType, "2");
                } else if (signalRecord.getFunStatus().equals("2")) {
                    /**
                     * 接收到的上一个开启信号已经关闭，則说明当前的关闭信号为冗余信号
                     * 重新创建一个信号记录
                     */
                    signalRecord = getSignalRecord(message, funType, "2");
                }else {
                    signalRecord.setFunStatus("2");
                }
                signalRecords.add(signalRecord);
            }
        } else {

            log.info("===========================  开启信号   ===========================");

            //其他信号，全为开启信号 ，直接创建信号记录
            for (Integer funType : funTypeList) {

                SignalRecord signalRecord = getSignalRecord(message, funType, "1");
                signalRecords.add(signalRecord);
            }
        }
    }


    /**
     * 根据信号获取功能type和是否具有结束信号
     *
     * @param telId
     * @return
     */
    public Map<String, Object> getHasStopSignalAndFunType(Integer telId) {
        Integer functionType = 0;
        boolean hasStopSignal = false;
        if (telId.equals(AlgorithmConstant.A3_TRANSFER_MACHINE_AUTOMATIC_CONFIRMATION)) {
            // A3移载机自动确认 开始/关闭指令
            functionType = FunctionTypeConstants.A3_TRANSFER_SLAB_DEVIATION;
            hasStopSignal = true;

        } else if (telId.equals(AlgorithmConstant.TRACK_STACKING_STEEL)) {
            /**
             * 辊道叠钢 开始/关闭指令
             * 注意叠钢识别 和 钢板测距使用的是同一个信号 同一个摄像头，算法发送两个结果
             */
            functionType = FunctionTypeConstants.SUPERPOSE_STEEL_IDENTIFICATION;
            hasStopSignal = true;

        } else if (telId.equals(AlgorithmConstant.STEEL_INSTALLATION_DEVIATION_30_4)) {
            //加热炉 装钢偏斜 开始指令
            functionType = FunctionTypeConstants.LOAD_STEEL_DEVIATION;

        } else if (telId.equals(AlgorithmConstant.STEEL_DRAWING_DEVIATION_30_4)) {
            // 加热炉 抽钢偏斜 开始指令
            functionType = FunctionTypeConstants.TAKE_STEEL_DEVIATION;

        } else if (telId.equals(AlgorithmConstant.FURNACE_DOOR_STATUS_30_4)) {
            // 加热炉 炉门定位 开始指令
            functionType = FunctionTypeConstants.STOVE_STATUS;

        } else if (telId.equals(AlgorithmConstant.STEEL_INSTALLATION_DEVIATION_30_5)) {
            // 加热炉 装钢偏斜 开始指令
            functionType = FunctionTypeConstants.LOAD_STEEL_DEVIATION;

        } else if (telId.equals(AlgorithmConstant.STEEL_DRAWING_DEVIATION_30_5)) {
            // 加热炉 抽钢偏斜 开始指令
            functionType = FunctionTypeConstants.TAKE_STEEL_DEVIATION;

        } else if (telId.equals(AlgorithmConstant.FURNACE_DOOR_STATUS_30_5)) {
            // 加热炉 炉门定位 开始指令
            functionType = FunctionTypeConstants.STOVE_STATUS;
        }
        HashMap<String, Object> map = new HashMap<>();
        map.put("telId", functionType);
        map.put("hasStopSignal", hasStopSignal);
        return map;
    }

    public String getPlcCode(String sourceIp) {
        String plcCode = "";
        if (sourceIp.equals(PlcConstants.PLC_1_IP)) {
            plcCode = PlcConstants.PLC_1_CODE;
        } else if (sourceIp.equals(PlcConstants.PLC_2_IP)) {
            plcCode = PlcConstants.PLC_2_CODE;
        } else if (sourceIp.equals(PlcConstants.PLC_3_IP)) {
            plcCode = PlcConstants.PLC_3_CODE;
        } else if (sourceIp.equals(PlcConstants.PLC_4_IP)) {
            plcCode = PlcConstants.PLC_4_CODE;
        } else if (sourceIp.equals("192.168.8.33")) {
            plcCode = PlcConstants.PLC_1_CODE;  // TODO: 2023-07-13 测试ip
        } else if (sourceIp.equals("127.0.0.1")) {
            plcCode = PlcConstants.PLC_1_CODE; // TODO: 2023-07-13 测试ip
        }
        return plcCode;
    }

    /**
     * 持久化信号与摄像头之间的关系
     *
     * @param code
     * @param id
     * @param stackingID
     */
    private void saveSignalCameraRelation(String code, Integer id, Integer stackingID) {
        log.info("=========================== 保存信号与摄像头之间的关系  ===========================");
        SignalCameraRelation signalCameraRelation = new SignalCameraRelation();
        signalCameraRelation.setCameraCode(code);
        signalCameraRelation.setCameraId(String.valueOf(id));
        signalCameraRelation.setSignalRecordId(String.valueOf(stackingID));
        signalCameraRelation.setCreateTime(LocalDateTime.now());

        iSignalCameraRelationService.save(signalCameraRelation);

    }

    private SignalRecord getSignalRecord(L1ToIdentifySysMessage message, Integer funType, String status) {
        SignalRecord signalRecord = new SignalRecord();

        signalRecord.setSignalResource(message.getClientIp());
        signalRecord.setSourcePort(message.getSourcePort());

        signalRecord.setPlanNo(message.getPlanNo());

        signalRecord.setPlcSignal(message.getPlcSignal());

        if (message.getDoorNum() != null) {
            signalRecord.setDoorNum(message.getDoorNum().toString());
        }

        if (status.equals("1")) {
            //信号开启时间
            signalRecord.setBeginTime(LocalDateTime.now());
        } else {
            //信号关闭时间
            signalRecord.setEndTime(LocalDateTime.now());
        }

        //获取netty的code
        SysNetty sysNetty = sysNettyService.getByIP(message.getClientIp(), message.getSourcePort());
        signalRecord.setNettyCode(sysNetty.getCode());

        signalRecord.setFunType(funType.toString());
        signalRecord.setFunStatus(status);//开启状态
        return signalRecord;
    }


    /**
     * 调用算法接口
     *
     * @param switchCameraAlgorithmBO 入参信息
     * @param url                     算法url
     * @param hasStopSignal           该信号是否具有结束信号
     * @param telId                   接收到的信号
     * @param signalRecord            持久化的信号记录
     * @param plcCode
     */

    public void requestAlgorithm(SwitchCameraAlgorithmBO switchCameraAlgorithmBO, String url, boolean hasStopSignal, int telId, SignalRecord signalRecord, String plcCode) {
        new Thread(() -> {

            String param = JSONObject.toJSONString(switchCameraAlgorithmBO);
            log.info("------------------------- 启停算法的指令为：{} --------------------------", param);

            String s = HttpRequestUtil.sendPost(url, param);
            log.info("------------------------- 请求url：{} --------------------------", url);

//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
            // TODO: 2023-07-13 为了对接plc的信号，先不调用算法接口 ，直接返回plc

//            sendMsgToPlc(signalRecord,switchCameraAlgorithmBO.getPlanNo(),switchCameraAlgorithmBO.getDoorNum(),1,telId,plcCode);

            if (org.springframework.util.StringUtils.isEmpty(s) || "null".equals(s)) {
                log.info("------------------------- 网络请求失败！ --------------------------");
                //调用算法异常
                signalRecord.setFunStatus("3");
                //更新信号记录为异常状态
                iSignalRecordService.updateById(signalRecord);

                return;
            }

            JSONObject o = JSONObject.parseObject(s);
            int resultCode = Integer.parseInt(o.getString("resultCode"));
            log.info("摄像头操作" + ((resultCode == -1) ? "失败" : "成功"));

            // TODO: 2023-07-14 信号调试通过之后放开以下代码
            if (resultCode == -1) {
                //调用算法异常
                signalRecord.setFunStatus("4");
                //更新信号记录为异常状态
                iSignalRecordService.updateById(signalRecord);

                // : 2023-07-13 如果调用算法失败，需要返给plc什么

            } else {
                //判断当前处理的是否为plc4的信号，  如果是，则对应的更改plc4客户端中对应的内存值
                if (plcCode.equals(PlcConstants.PLC_4_CODE)){
                    //当前操作的是plc4信号
                    BootNettyNewLink.runStatus=new AtomicInteger(1);//开启状态
                }else {
                    /**
                     * 操作的信号为关闭信号，或者是只有开启的信号
                     * 修改信号日志
                     */
                    if ((switchCameraAlgorithmBO.getState() == 2) || (switchCameraAlgorithmBO.getState() == 1 && !hasStopSignal)) {
                        //解决多次发送结束信号，======  没发开始，一直发关闭，
                        LocalDateTime beginTime = signalRecord.getBeginTime();
                        if (null != beginTime) {
                            // : 2023-07-11  冗余的信号来源，无需进行处理，  停止当前线程
                            /**
                             * 如果是关闭信号，等待5s，判断是否接收到识别记录
                             * 当前为开启信号，但是不会关闭信号,需要判断是否在5s内收到了算法的识别记录，如果没有，则直接返回正常
                             */
                            //当前信号不是关闭状态
                            log.info("=========================== {}关闭信号,等待 5 s  ===========================", switchCameraAlgorithmBO.getFunctions());
                            try {
                                Thread.sleep(5 * 1000);
                                SignalRecord record = iSignalRecordService.getById(signalRecord.getId());
                                if (!record.getFunStatus().equals("2")) {
                                    /**
                                     * 当前的信号并不是关闭状态，说明没有收到对应的识别结果，
                                     * 直接回调plc 正常
                                     */
//                                   TODO: 2023-07-18  更新信号状态
                                    sendMsgToPlc(signalRecord, switchCameraAlgorithmBO.getPlanNo(), switchCameraAlgorithmBO.getDoorNum(), 1, telId, plcCode);
                                }
                                //当前的信号已经为关闭状态，不需要在额外进行处理
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }

                    }
                }

            }

        }).start();
    }

    /**
     * 向plc发送结果
     *
     * @param signalRecord 信号记录
     * @param planNo       板坯号
     * @param doorNum      炉门编号
     * @param result       识别结果 1 正常 2 异常
     * @param telId        信号id
     */
    private void sendMsgToPlc(SignalRecord signalRecord, String planNo, String doorNum, Integer result, Integer telId, String plcCode) {

//        String nettyCode = signalRecord.getNettyCode();
//
//        final ReentrantLock lock = new ReentrantLock();

        Integer toPlcTelId = getToPlcTelId(telId);
        IdentifySysToL1Message identifySysToL1Message = new IdentifySysToL1Message();
        identifySysToL1Message.setTelID(toPlcTelId);//电文号
        identifySysToL1Message.setPlanNo(planNo);//版批号
        identifySysToL1Message.setDoorNum(doorNum);//炉门编号
        identifySysToL1Message.setResult(result);// 1 正常 2 异常
//        identifySysToL1Message.setSpare();
        String jsonString = JSONObject.toJSONString(identifySysToL1Message);
        int length = jsonString.getBytes().length;

        //填充真实数据的字节长度
        identifySysToL1Message.setTelLen(length);

        String msgJSON = JSONObject.toJSONString(identifySysToL1Message);


        if (!plcCode.equals(PlcConstants.PLC_4_CODE)) {
            clientSendMessage(plcCode, identifySysToL1Message);
        }


    }

    /**
     * 返回给plc的信号 plcId
     *
     * @param telId 接收plc的信号id
     * @return
     */
    public Integer getToPlcTelId(Integer telId) {
        Integer plcId = 0;
        if (telId.equals(AlgorithmConstant.A3_TRANSFER_MACHINE_AUTOMATIC_CONFIRMATION)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_A3_TRANSFER_MACHINE;

        } else if (telId.equals(AlgorithmConstant.TRACK_STACKING_STEEL)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_TRACK_STACKING;

        } else if (telId.equals(AlgorithmConstant.STEEL_INSTALLATION_DEVIATION_30_4)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_INSTALLATION_DEVIATION_30_4;

        } else if (telId.equals(AlgorithmConstant.STEEL_DRAWING_DEVIATION_30_4)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_DRAWING_DEVIATION_30_4;

        } else if (telId.equals(AlgorithmConstant.FURNACE_DOOR_STATUS_30_4)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_DOOR_STATUS_30_4;

        } else if (telId.equals(AlgorithmConstant.STEEL_INSTALLATION_DEVIATION_30_5)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_INSTALLATION_DEVIATION_30_5;

        } else if (telId.equals(AlgorithmConstant.STEEL_DRAWING_DEVIATION_30_5)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_DRAWING_DEVIATION_30_5;

        } else if (telId.equals(AlgorithmConstant.FURNACE_DOOR_STATUS_30_5)) {
            plcId = PlcConstants.RETURN_TO_MESSAGE_DOOR_STATUS_30_5;

        }
        return plcId;
    }

    /**
     * 接收到心跳
     *
     * @param message
     */
    @Override
    public void receiveHeartbeat(L1ToIdentifySysMessage message) {

    }

    @Override
    public void sendHeartbeat() {

    }
}
